/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.mybatis;

import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.session.Configuration;

import com.jianggujin.dbfly.mybatis.builder.JMappedStatementBuilderRegistry;
import com.jianggujin.dbfly.mybatis.dialect.JDialect;
import com.jianggujin.dbfly.mybatis.dialect.JDialectRegistry;
import com.jianggujin.dbfly.mybatis.entity.JEntityResolver;
import com.jianggujin.dbfly.mybatis.value.JValueGenerator;
import com.jianggujin.dbfly.util.JDBFlyException;
import com.jianggujin.dbfly.util.JExceptionUtils;
import com.jianggujin.dbfly.util.JObjectRef;
import com.jianggujin.dbfly.util.JStringUtils;
import com.jianggujin.dbfly.util.JStyle;

/**
 * JDBFly配置
 * 
 * @author jianggujin
 *
 */
public class JDBFlyConfiguration {

    /**
     * 方言
     */
    private volatile JObjectRef<JDialect> dialectRef = null;
    /**
     * 全局schema
     */
    private String schema;
    /**
     * 字段转换风格，默认驼峰转下划线
     */
    private JStyle style;
    /**
     * 是否设置 javaType
     */
    private boolean useJavaType;
    /**
     * 是否增加空字符串判断，仅当Java类型为字符串时生效
     */
    private boolean notEmpty;
    /**
     * 全局需要排除的不需要查询的列
     */
    private String[] excludeSelects;
    /**
     * 全局需要排除的不需要插入的列
     * 
     */
    private String[] excludeInserts;
    /**
     * 全局需要排除的不需要修改的列
     * 
     */
    private String[] excludeUpdates;
    /**
     * 全局Value生成器的列
     * 
     */
    private String[] valueGeneratorProperties;
    /**
     * Value生成器实现类
     */
    private Class<? extends JValueGenerator> valueGeneratorClass;
    /**
     * 方言注册器
     */
    private final JDialectRegistry dialectRegistry;
    /**
     * MappedStatement构造器注册器
     */
    private final JMappedStatementBuilderRegistry mappedStatementBuilderRegistry;
    /**
     * 实体解析器
     */
    private final JEntityResolver entityResolver;
    private final Configuration configuration;

    public JDBFlyConfiguration(Configuration configuration) {
        this.configuration = configuration;
        this.dialectRegistry = new JDialectRegistry();
        this.mappedStatementBuilderRegistry = new JMappedStatementBuilderRegistry();
        this.entityResolver = new JEntityResolver(this);
    }

    /**
     * 获得方言
     * 
     * @return
     */
    public JDialect getDialect() {
        this.ensureDialect();
        return dialectRef.getValue();
    }

    /**
     * 设置方言
     * 
     * @param dialect
     */
    public void setDialect(JDialect dialect) {
        this.dialectRef = new JObjectRef<>(dialect);
    }

    /**
     * 获得全局需要排除的不需要查询的列
     * 
     * @return
     */
    public String[] getExcludeSelects() {
        return excludeSelects;
    }

    /**
     * 设置全局需要排除的不需要查询的列
     * 
     * @param excludeSelects
     */
    public void setExcludeSelects(String[] excludeSelects) {
        this.excludeSelects = excludeSelects;
    }

    /**
     * 获得全局需要排除的不需要插入的列
     * 
     * @return
     */
    public String[] getExcludeInserts() {
        return excludeInserts;
    }

    /**
     * 设置全局需要排除的不需要插入的列
     * 
     * @param excludeInserts
     */
    public void setExcludeInserts(String[] excludeInserts) {
        this.excludeInserts = excludeInserts;
    }

    /**
     * 获得全局需要排除的不需要修改的列
     * 
     * @return
     */
    public String[] getExcludeUpdates() {
        return excludeUpdates;
    }

    /**
     * 设置全局需要排除的不需要修改的列
     * 
     * @param excludeUpdates
     */
    public void setExcludeUpdates(String[] excludeUpdates) {
        this.excludeUpdates = excludeUpdates;
    }

    /**
     * 全局Value生成器的列
     * 
     * @return
     */
    public String[] getValueGeneratorProperties() {
        return valueGeneratorProperties;
    }

    /**
     * 全局Value生成器的列
     * 
     * @param valueGeneratorProperties
     */
    public void setValueGeneratorProperties(String[] valueGeneratorProperties) {
        this.valueGeneratorProperties = valueGeneratorProperties;
    }

    /**
     * Value生成器实现类
     * 
     * @return
     */
    public Class<? extends JValueGenerator> getValueGeneratorClass() {
        return valueGeneratorClass;
    }

    /**
     * Value生成器实现类
     * 
     * @param valueGeneratorClass
     */
    public void setValueGeneratorClass(Class<? extends JValueGenerator> valueGeneratorClass) {
        this.valueGeneratorClass = valueGeneratorClass;
    }

    /**
     * 获得字段转换风格，默认驼峰转下划线
     * 
     * @return
     */
    public JStyle getStyle() {
        return this.style == null ? JStyle.camelhump : this.style;
    }

    /**
     * 设置字段转换风格，默认驼峰转下划线
     * 
     * @param style
     */
    public void setStyle(JStyle style) {
        this.style = style;
    }

    /**
     * 获得全局schema
     * 
     * @return
     */
    public String getSchema() {
        return schema;
    }

    /**
     * 设置全局schema
     * 
     * @param schema
     */
    public void setSchema(String schema) {
        this.schema = schema;
    }

    /**
     * 获得MappedStatement构造器注册器
     * 
     * @return
     */
    public JMappedStatementBuilderRegistry getMappedStatementBuilderRegistry() {
        return mappedStatementBuilderRegistry;
    }

    /**
     * 实体解析器
     * 
     * @return
     */
    public JEntityResolver getEntityResolver() {
        return entityResolver;
    }

    /**
     * 
     * 方言注册器
     * 
     * @return
     */
    public JDialectRegistry getDialectRegistry() {
        return dialectRegistry;
    }

    public Configuration getConfiguration() {
        return configuration;
    }

    /**
     * 是否设置 javaType
     * 
     * @return
     */
    public boolean isUseJavaType() {
        return useJavaType;
    }

    /**
     * 
     * 是否设置 javaType
     * 
     * @param useJavaType
     */
    public void setUseJavaType(boolean useJavaType) {
        this.useJavaType = useJavaType;
    }

    /**
     * 是否增加空字符串判断，仅当Java v类型为字符串时生效
     * 
     * @return
     */
    public boolean isNotEmpty() {
        return notEmpty;
    }

    /**
     * 是否增加空字符串判断，仅当Java类型为字符串时生效
     * 
     * @param notEmpty
     */
    public void setNotEmpty(boolean notEmpty) {
        this.notEmpty = notEmpty;
    }

    /**
     * 配置属性
     *
     * @param properties
     */
    @SuppressWarnings("unchecked")
    public void setProperties(Properties properties) {
        if (properties == null) {
            // 默认驼峰
            this.style = JStyle.camelhump;
            return;
        }
        String dialect = properties.getProperty("dialect");
        if (JStringUtils.isNotEmpty(dialect)) {
            try {
                setDialect((JDialect) Class.forName(dialect).newInstance());
            } catch (Exception e) {
                throw JExceptionUtils.handleException(e);
            }
        }
        String dialects = properties.getProperty("dialects");
        if (JStringUtils.isNotEmpty(dialects)) {
            try {
                for (String dialectClass : dialects.split(",")) {
                    if (JStringUtils.isNotEmpty(dialectClass)) {
                        Class<?> clazz = Class.forName(dialectClass);
                        this.dialectRegistry.getDialectClasses().replaceOrAddLast(clazz.getSimpleName(),
                                (Class<? extends JDialect>) clazz);
                    }
                }
            } catch (ClassNotFoundException e) {
                throw JExceptionUtils.handleException(e);
            }
        }

        String styleStr = properties.getProperty("style");
        if (JStringUtils.isNotEmpty(styleStr)) {
            try {
                this.style = JStyle.valueOf(styleStr);
            } catch (IllegalArgumentException e) {
                throw new JDBFlyException("{}不是合法的Style值!", styleStr);
            }
        } else {
            // 默认驼峰
            this.style = JStyle.camelhump;
        }
        String schema = properties.getProperty("schema");
        if (JStringUtils.isNotEmpty(schema)) {
            this.schema = schema;
        }
        // 是否设置 javaType，true 时如 {id, javaType=java.lang.Long}
        this.useJavaType = Boolean.valueOf(properties.getProperty("useJavaType"));
        this.notEmpty = Boolean.valueOf(properties.getProperty("notEmpty"));
        String exludeSelects = properties.getProperty("exludeSelects");
        if (JStringUtils.isNotEmpty(exludeSelects)) {
            setExcludeSelects(exludeSelects.split(","));
        }
        String excludeInserts = properties.getProperty("excludeInserts");
        if (JStringUtils.isNotEmpty(excludeInserts)) {
            setExcludeInserts(excludeInserts.split(","));
        }
        String excludeUpdates = properties.getProperty("excludeUpdates");
        if (JStringUtils.isNotEmpty(excludeUpdates)) {
            setExcludeUpdates(excludeUpdates.split(","));
        }
        String valueGeneratorProperties = properties.getProperty("valueGeneratorProperties");
        if (JStringUtils.isNotEmpty(valueGeneratorProperties)) {
            setValueGeneratorProperties(valueGeneratorProperties.split(","));
        }
        String valueGeneratorClass = properties.getProperty("valueGeneratorClass");
        if (JStringUtils.isNotEmpty(valueGeneratorClass)) {
            try {
                this.valueGeneratorClass = (Class<? extends JValueGenerator>) Class.forName(valueGeneratorClass);
            } catch (ClassNotFoundException e) {
                throw new JDBFlyException(e);
            }
        }
    }

    /**
     * 确保方言已经解析
     */
    private void ensureDialect() {
        if (this.dialectRef == null) {
            synchronized (this) {
                if (this.dialectRef == null) {
                    JDialect dialect = null;
                    try (Connection conn = this.configuration.getEnvironment().getDataSource().getConnection()) {
                        dialect = this.dialectRegistry.getDialect(conn.getMetaData(), false);
                    } catch (Exception e) {
                        throw JExceptionUtils.handleException(e);
                    }
                    this.dialectRef = new JObjectRef<JDialect>(dialect);
                }
            }
        }
    }
}
